package org.alicuu;

import org.alicuu.mvc.route.Route;
import org.alicuu.mvc.route.RouteMatcher;
import org.alicuu.mvc.route.Routers;
import org.alicuu.mvc.servlet.Request;
import org.alicuu.mvc.servlet.Response;
import org.alicuu.toolkit.PathUtil;
import org.alicuu.toolkit.ReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;


/**
 * Created by IntelliJ IDEA.
 * User: Danny
 * Date: 16/07/06
 * Time: 09:13
 */
public class KumamonFilter implements Filter {

    private static final Logger log = LoggerFactory.getLogger(KumamonFilter.class);
    private RouteMatcher routeMatcher = new RouteMatcher(new ArrayList<>());
    private ServletContext servletContext;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Kumamon kumamon = Kumamon.getInstance();
        if (!kumamon.isInit()) {
            String className = filterConfig.getInitParameter("bootstrap");
            Bootstrap bootstrap = this.getBootstrap(className);
            bootstrap.init(kumamon);

            Routers routers = kumamon.getRouters();
            if (routers != null)
                routeMatcher.setRoutes(routers.getRoutes());

            servletContext = filterConfig.getServletContext();

            kumamon.setInit(true);
        }
    }

    private Bootstrap getBootstrap(String className) {
        if (className != null) {
            try {
                Class<?> clazz = Class.forName(className);
                return (Bootstrap) clazz.newInstance();
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
        throw new RuntimeException("init bootstrap class error!");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        request.setCharacterEncoding(Const.DEFAULT_CHAR_SET);
        response.setCharacterEncoding(Const.DEFAULT_CHAR_SET);

        String uri = PathUtil.getRelativePath(request);

        log.info("Request URI：" + uri);

        Route route = routeMatcher.findRoute(uri);

        if (route != null)
            handle(request, response, route);
        else
            filterChain.doFilter(request, response);


    }

    private void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Route route) {
        Request request = new Request(httpServletRequest);
        Response response = new Response(httpServletResponse);
        KumamonContext.initContext(servletContext, request, response);

        Object controller = route.getController();
        Method actionMethod = route.getAction();
        executeMethod(controller, actionMethod, request, response);
    }

    private Object executeMethod(Object controller, Method method, Request request, Response response) {
        int len = method.getParameterTypes().length;
        method.setAccessible(true);
        if (len > 0) {
            Object[] args = getArgs(request, response, method.getParameterTypes());
            return ReflectUtil.invokeMethod(controller, method, args);
        } else
            return ReflectUtil.invokeMethod(controller, method);

    }

    private Object[] getArgs(Request request, Response response, Class<?>[] params) {
        int len = params.length;
        Object[] args = new Object[len];

        for (int i = 0; i < len; i++) {
            Class<?> paramTypeClazz = params[i];
            if (paramTypeClazz.getName().equals(Request.class.getName()))
                args[i] = request;
            if (paramTypeClazz.getName().equals(Response.class.getName()))
                args[i] = response;
        }

        return args;
    }

    @Override
    public void destroy() {

    }
}
